package cn.bugDance.infrastructure.persistent.repository;

import cn.bugDance.domain.award.model.aggregate.GiveOutPrizesAggregate;
import cn.bugDance.domain.award.model.aggregate.UserAwardRecordAggregate;
import cn.bugDance.domain.award.model.entity.TaskEntity;
import cn.bugDance.domain.award.model.entity.UserAwardRecordEntity;
import cn.bugDance.domain.award.model.entity.UserCreditAwardEntity;
import cn.bugDance.domain.award.model.valobj.UserCreditAccountState;
import cn.bugDance.domain.award.repository.IAwardRepository;
import cn.bugDance.domain.strategy.model.entity.AwardEntity;
import cn.bugDance.infrastructure.event.EventPublisher;
import cn.bugDance.infrastructure.persistent.dao.*;
import cn.bugDance.infrastructure.persistent.po.Award;
import cn.bugDance.infrastructure.persistent.po.Task;
import cn.bugDance.infrastructure.persistent.po.UserAwardRecord;
import cn.bugDance.infrastructure.persistent.po.UserCreditAccount;
import cn.bugDance.infrastructure.persistent.redis.IRedisService;
import cn.bugDance.types.common.Constants;
import cn.bugDance.types.enums.ResponseCode;
import cn.bugDance.types.exception.AppException;
import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

@Repository
@Slf4j
public class AwardRepository implements IAwardRepository {

    @Resource
    private ITaskDao taskDao;
    @Resource
    private IUserAwardRecordDao userAwardRecordDao;
    @Resource
    private IActivityPartakeOrderDao activityPartakeOrderDao;
    @Resource
    private IDBRouterStrategy dbRouter;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private ThreadPoolExecutor threadPoolExecutor;
    @Resource
    private EventPublisher eventPublisher;
    @Resource
    private IRedisService redisService;
    @Resource
    private IAwardDao awardDao;
    @Resource
    private IUserCreditAccountDao userCreditAccountDao;

    /**
     * 1. 事务->
     *    保存中奖记录
     *    保存Task表
     *    修改活动参与订单状态为已使用
     * 2.  MQ发奖
     *     成功:
     *     事务->
     *     修改Task状态
     *     修改记录状态
     *     失败:
     *     回滚Task状态
     * @param userAwardRecordAggregate 用户中奖记录聚合
     */
    @Override
    public void saveAwardRecord(UserAwardRecordAggregate userAwardRecordAggregate) {

        try{
            TaskEntity taskEntity = userAwardRecordAggregate.getTaskEntity();
            UserAwardRecordEntity userAwardRecordEntity = userAwardRecordAggregate.getUserAwardRecordEntity();

            UserAwardRecord userAwardRecord = new UserAwardRecord();
            userAwardRecord.setAwardId(userAwardRecordEntity.getAwardId());
            userAwardRecord.setUserId(userAwardRecordEntity.getUserId());
            userAwardRecord.setActivityId(userAwardRecordEntity.getActivityId());
            userAwardRecord.setStrategyId(userAwardRecordEntity.getStrategyId());
            userAwardRecord.setOrderId(userAwardRecordEntity.getOrderId());
            userAwardRecord.setAwardState(userAwardRecordEntity.getAwardState().getCode());
            userAwardRecord.setAwardTitle(userAwardRecordEntity.getAwardTitle());
            userAwardRecord.setAwardTime(userAwardRecordEntity.getAwardTime());

            Task task = new Task();
            task.setTopic(taskEntity.getTopic());
            task.setState(taskEntity.getState().getCode());
            task.setMessage(JSON.toJSONString(taskEntity.getMessage()));
            task.setMessageId(taskEntity.getMessage().getId());
            task.setUserId(taskEntity.getUserId());

            dbRouter.doRouter(userAwardRecordEntity.getUserId());
            transactionTemplate.execute(status -> {
               try{
                   userAwardRecordDao.insert(userAwardRecord);
                   taskDao.insert(task);

                   int count = activityPartakeOrderDao.updateState(userAwardRecordEntity.getOrderId());
                   if(count <= 0){
                       status.setRollbackOnly();
                       log.warn("【奖品仓储层-AwardRepository】: 更新参与订单状态失败, orderId: {}", userAwardRecordEntity.getOrderId());
                       throw new AppException(ResponseCode.ACTIVITY_PARTAKE_ORDER_STATE_ERROR.getCode(), ResponseCode.ACTIVITY_ACCOUNT_TOTAL_COUNT_ERROR.getInfo());
                   }
                   return 0;
               } catch (DuplicateKeyException e){
                   status.setRollbackOnly();
                   log.warn("【奖品仓储层-AwardRepository】: 插入中奖记录错误, orderId: {}, message: {}", userAwardRecordEntity.getOrderId(), e.getMessage());
                   throw new AppException(ResponseCode.DUPLICATE_KEY_EXCEPTION.getCode(), ResponseCode.DUPLICATE_KEY_EXCEPTION.getInfo());
               }
            });
        }finally {
            dbRouter.clear();
        }

        // MQ发奖
        TaskEntity taskEntity = userAwardRecordAggregate.getTaskEntity();
        threadPoolExecutor.execute(() -> {
            try{
                eventPublisher.publish(taskEntity.getTopic(), taskEntity.getMessage());
                dbRouter.doRouter(userAwardRecordAggregate.getUserAwardRecordEntity().getUserId());
                taskDao.updateTaskStateSuccess(taskEntity.getMessage().getId());
            }catch (Exception e){
                log.warn("【奖品仓储层-AwardRepository】: 发奖MQ发送失败");
                taskDao.updateTaskStateFail(taskEntity.getMessage().getId());
            }finally {
                dbRouter.clear();
            }
        });
    }

    @Override
    public AwardEntity queryAwardByAwardId(Integer awardId) {
        String cacheKey = Constants.Redis.AWARD + awardId;
        AwardEntity awardEntity = redisService.getValue(cacheKey);
        if(null != awardEntity) return awardEntity;

        Award award = awardDao.getAwardByAwardId(awardId);
        awardEntity = new AwardEntity();
        awardEntity.setAwardId(awardId);
        awardEntity.setAwardKey(award.getAwardKey());
        awardEntity.setAwardConfig(award.getAwardConfig());
        awardEntity.setAwardDesc(award.getAwardDesc());

        redisService.setValue(cacheKey, awardEntity);
        return awardEntity;
    }

    @Override
    public void giveOutPrizes(GiveOutPrizesAggregate giveOutPrizesAggregate) {
        try{
            String userId = giveOutPrizesAggregate.getUserId();
            UserCreditAwardEntity userCreditAwardEntity = giveOutPrizesAggregate.getUserCreditAwardEntity();
            UserAwardRecordEntity userAwardRecordEntity = giveOutPrizesAggregate.getUserAwardRecordEntity();

            UserAwardRecord userAwardRecord = new UserAwardRecord();
            userAwardRecord.setOrderId(userAwardRecordEntity.getOrderId());
            userAwardRecord.setAwardState(userAwardRecordEntity.getAwardState().getCode());


            UserCreditAccount userCreditAccount = new UserCreditAccount();
            userCreditAccount.setUserId(userId);
            userCreditAccount.setTotalAmount(userCreditAwardEntity.getCreditAmount());
            userCreditAccount.setAvailableAmount(userCreditAwardEntity.getCreditAmount());
            userCreditAccount.setState(UserCreditAccountState.OPEN.getCode());

            dbRouter.doRouter(userId);
            transactionTemplate.execute(status -> {
                try{
                    userAwardRecordDao.updateUserAwardRecordState(userAwardRecord);

                    int count = userCreditAccountDao.updateUserCreditAccount(userCreditAccount);
                    if(count <= 0){
                        userCreditAccountDao.insert(userCreditAccount);
                    }
                    return 0;
                }catch (DuplicateKeyException e){
                    log.warn("【奖品仓储层-AwardRepository】: 写入积分账户失败, userId: {}", userId);
                    status.setRollbackOnly();
                    throw new AppException(ResponseCode.DUPLICATE_KEY_EXCEPTION.getCode(), ResponseCode.DUPLICATE_KEY_EXCEPTION.getInfo());
                }
            });
        }finally {
            dbRouter.clear();
        }
    }

    @Override
    public List<UserAwardRecordEntity> queryUserAwardRecords(String userId) {
        UserAwardRecord userAwardRecordReq = new UserAwardRecord();;
        userAwardRecordReq.setUserId(userId);
        try{
            dbRouter.doRouter(userId);
            List<UserAwardRecord> userAwardRecords = userAwardRecordDao.queryUserRecords(userAwardRecordReq);
            if(userAwardRecords == null || userAwardRecords.isEmpty()) return null;
            List<UserAwardRecordEntity> userAwardRecordEntities = new ArrayList<>();
            for (UserAwardRecord userAwardRecord : userAwardRecords) {
                UserAwardRecordEntity userAwardRecordEntity = new UserAwardRecordEntity();
                userAwardRecordEntity.setOrderId(userAwardRecord.getOrderId());
                userAwardRecordEntity.setUserId(userAwardRecord.getUserId());
                userAwardRecordEntity.setAwardTitle(userAwardRecord.getAwardTitle());
                userAwardRecordEntities.add(userAwardRecordEntity);
            }
            return userAwardRecordEntities;
        }catch (Exception e){
            throw new AppException(ResponseCode.UN_ERROR.getCode(), ResponseCode.UN_ERROR.getInfo());
        }
        finally {
            dbRouter.clear();
        }
    }

    @Override
    public List<UserAwardRecordEntity> queryUserAwardRecordsToAll(Long activityId, Date nowDate) {
        int countDb = dbRouter.dbCount();
        int countTb = dbRouter.tbCount();
        List<CompletableFuture<List<UserAwardRecord>>> completableFutures = new ArrayList<>();
        for(int i = 1; i <= countDb; i++){
            for(int j = 0; j < countTb; j++){
                int finalDbIdx = i;
                int finalTbIdx = j;
                CompletableFuture<List<UserAwardRecord>> completableFuture = this.queryUserAwardRecordsByDate(activityId, nowDate, finalDbIdx, finalTbIdx);
                completableFutures.add(completableFuture);
            }
        }
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0]));
        List<UserAwardRecordEntity> userAwardRecordEntities = new ArrayList<>();
        try{
            allFutures.join();
            for (CompletableFuture<List<UserAwardRecord>> future : completableFutures) {
                List<UserAwardRecord> result = future.getNow(null);
                if(result != null){
                    for (UserAwardRecord userAwardRecord : result) {
                        UserAwardRecordEntity userAwardRecordEntity = new UserAwardRecordEntity();
                        userAwardRecordEntity.setAwardTitle(userAwardRecord.getAwardTitle());
                        userAwardRecordEntity.setUserId(userAwardRecord.getUserId());
                        userAwardRecordEntities.add(userAwardRecordEntity);
                    }
                }
            }
        }catch (Exception e){
            throw new AppException(ResponseCode.UN_ERROR.getCode(), ResponseCode.UN_ERROR.getInfo());
        }
        return userAwardRecordEntities;
    }


    private CompletableFuture<List<UserAwardRecord>> queryUserAwardRecordsByDate(Long activityId, Date nowDate, int finalDbIdx, int finalTbIdx) {
        return CompletableFuture.supplyAsync(() -> {
            try{
                dbRouter.setDBKey(finalDbIdx);
                dbRouter.setTBKey(finalTbIdx);
                return userAwardRecordDao.queryUserRecordsByDate(activityId, nowDate);
            }finally {
                dbRouter.clear();
            }
        }, threadPoolExecutor).handle((result, ex) -> {
            if (ex != null) {
                log.error("异常", ex);
                return Collections.emptyList(); // 返回一个默认值
            }
            if(result == null) return Collections.emptyList();
            return result;
        });
    }
}
